package com.atguigu.srb.core.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.srb.core.listener.ExcelDictDTOListener;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.atguigu.srb.core.mapper.DictMapper;
import com.atguigu.srb.core.service.DictService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 数据字典 服务实现类
 * </p>
 *
 * @author Ahaohan
 * @since 2023-06-22
 */
@Slf4j
@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    @Resource
    private RedisTemplate redisTemplate;

    //当前Service下的Mapper不需要再注入，直接用baseMapper

    /**
     * 事务如果成功就全导出，失败就全回撤
     * 在所有的异常中都回滚
     * 此处添加了事务处理，默认情况下rollbackFor = RuntimeException.class
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void importData(InputStream inputStream) {
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(baseMapper)).sheet().doRead();
        log.info("Excel导入成功");
    }

    @Override
    public List<ExcelDictDTO> listDictData() {

        List<Dict> dictList = baseMapper.selectList(null);
        //创建ExcelDictDTO，将Dict列表转换成ExcelDictDTO列表；指定列表长度与dictList相同，如果不够的话再扩容浪费应用程序的执行效率
        ArrayList<ExcelDictDTO> excelDictDTOList = new ArrayList<>(dictList.size());
        //遍历dictList，所有的dictList中的数据项都会被封装到dict对象中
        dictList.forEach(dict -> {
            ExcelDictDTO excelDictDTO = new ExcelDictDTO();
            //对象拷贝，把dict的同名属性拷贝到excelDictDTO
            BeanUtils.copyProperties(dict,excelDictDTO);
            excelDictDTOList.add(excelDictDTO);
        });

        return excelDictDTOList;
    }

    @Override
    public List<Dict> listByParentId(Long parentId) {

        //如果Redis服务出现异常（连不上），不让应用程序抛异常，还可以直接从数据库去查
        try {
            //首先查询Redis中是否存在数据列表
            List<Dict> dictList = (List<Dict>)redisTemplate.opsForValue().get("srb:core:dictList" + parentId);
            //出现异常 按 Ctrl+Alt+t，选 try/catch 捕获异常；还是提示异常，强制转换类型，不再提示异常
            if(dictList != null){
                //如果存在则从Redis中直接返回数据列表
                log.info("从Redis中获取数据列表");
                return  dictList;
            }
        } catch (Exception e) {
            //e.printStackTrace();
            log.error("Redis服务器异常：" + ExceptionUtils.getStackTrace(e));
            //ExceptionUtils.getStackTrace(e) 拿到e对象的错误跟踪栈字符串
        }

        //如果不存在则查询数据库
        log.info("从数据库中获取数据列表");
        QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();
        dictQueryWrapper.eq("parent_id",parentId);
        List<Dict> dictList = baseMapper.selectList(dictQueryWrapper);
        // 填充hasChildren字段
        dictList.forEach(dict -> {
            //判断当前节点是否有子节点，找到当前的dict下级有没有子节点
            boolean hasChildren = this.hasChildren(dict.getId());
            dict.setHasChildren(hasChildren);
        });

        try {
            log.info("将数据存入Redis");
            //将数据存入Redis
            redisTemplate.opsForValue().set("srb:core:dictList" + parentId, dictList, 5, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("Redis服务器异常：" + ExceptionUtils.getStackTrace(e));
        }

        //返回数据列表
        return dictList;
    }

    @Override
    public List<Dict> findByDictCode(String dictCode) {
        QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();
        dictQueryWrapper.eq("dict_code",dictCode);
        Dict dict = baseMapper.selectOne(dictQueryWrapper);
        return this.listByParentId(dict.getId());
    }

    @Override
    public String getNameByParentDictCodeAndValue(String dictCode, Integer value) {

        QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();
        dictQueryWrapper.eq("dict_code",dictCode);
        Dict parentDict = baseMapper.selectOne(dictQueryWrapper);

        if(parentDict == null){
            return "";
        }

        dictQueryWrapper = new QueryWrapper<>();
        //初始化一下QueryWrapper<Dict>
        dictQueryWrapper
                .eq("parent_id",parentDict.getId())
                .eq("value",value);
        Dict dict = baseMapper.selectOne(dictQueryWrapper);
        //selectOne因为一个value只对应一个值

        if (dict == null){
            return "";
        }

        return dict.getName();
    }

    /**
     * 辅助方法 判断当前id所在的节点下是否有子节点
     * @param id
     * @return
     */
    private boolean hasChildren(Long id){
        QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();
        dictQueryWrapper.eq("parent_id",id);
        Integer count = baseMapper.selectCount(dictQueryWrapper);
        if (count.intValue() > 0){
            return true;
        }
        return false;
    }
}
